#include "mapscene.h"

//===============================================================
// Initialize the map scene.
//===============================================================
CMapScene::CMapScene()
	:	m_pMap(NULL),
		m_pTargetActor(NULL),
		m_pTileset(NULL)
{
	// TODO: CEngine needs some static functions to get the viewport width and height.

	m_nMaxTileWidth = (800 / 32) + 2;
	m_nMaxTileHeight = (600 / 32) + 2;
}

//===============================================================
// Adds the specified actor the current map.
//===============================================================
void CMapScene::AddActor(u_int nId, CActor *pActor)
{
	if (!pActor)
		return;

	m_mapActors.insert(std::pair<u_int, CActor *>(nId, pActor));
}

//===============================================================
// Removes the specified actor from the current map.
//===============================================================
void CMapScene::RemoveActor(u_int nId)
{
	m_mapActors.erase(nId);
}

//===============================================================
// Gets the actor with the specified id.
//===============================================================
CActor *CMapScene::GetActorById(u_int nId)
{
	CActorMap::iterator it = m_mapActors.find(nId);
	if (it == m_mapActors.end())
		return NULL;

	return it->second;
}

struct ACTORINFO {
	int			x;
	int			y;
	CAnimation *ani;
	const char *name;
};

bool ACTORINFO_Sort(ACTORINFO *i, ACTORINFO *j) { return (i->y < j->y); }

#include <vector>
#include <algorithm>

#include "../engine.h"

//===============================================================
// Renders the scene.
//===============================================================
void CMapScene::Render()
{
	if (!m_pMap)
		return;

	glEnable(GL_BLEND);

	int targetTileX = 0, targetTileY = 0, targetOffsetX = 0, targetOffsetY = 0;
	if (m_pTargetActor)
	{
		targetTileX   = m_pTargetActor->GetX() / 32;
		targetTileY   = m_pTargetActor->GetY() / 32;
		targetOffsetX = m_pTargetActor->GetOffsetX();
		targetOffsetY = m_pTargetActor->GetOffsetY();
	}

	int targetX = 400 - 16;
	int targetY = 300 - 16;

	int mapOffsetX = (targetX - (targetTileX * 32)) - targetOffsetX;
	int mapOffsetY = (targetY - (targetTileY * 32)) - targetOffsetY;

	int startX = targetTileX - (m_nMaxTileWidth / 2);
	if (startX < 0) startX = 0;

	int startY = targetTileY - (m_nMaxTileHeight / 2);
	if (startY < 0) startY = 0;

	int endX = startX + m_nMaxTileWidth;
	if (endX > m_pMap->GetWidth()) endX = m_pMap->GetWidth();

	int endY = startY + m_nMaxTileHeight;
	if (endY > m_pMap->GetHeight()) endY = m_pMap->GetHeight();

	/* Draw the floor layers. */
	if (m_pTileset)
	{ 
		m_pTileset->Bind();
		glColor3f(1.0f, 1.0f, 1.0f);

		for (int z = 0; z < m_pMap->GetLayerCount() - 1; z++) 
		{
			for (int x = startX; x < endX + 1; x++)
			{
				if (x >= m_pMap->GetWidth())
					continue;

				for (int y = startY; y < endY + 1; y++) 
				{
					TILE *t = m_pMap->At(x, y, z);
					if (!t)
						continue;

					float left   = m_flTileWidth * t->x;
					float top    = m_flTileHeight * t->y;
					float right  = left + m_flTileWidth;
					float bottom = top + m_flTileHeight;

					glBegin(GL_QUADS);
						glTexCoord2f(left, top);
						glVertex2d((mapOffsetX + x * 32),  (mapOffsetY + y * 32));

						glTexCoord2f(right, top);
						glVertex2d((mapOffsetX + x * 32) + 32, (mapOffsetY + y * 32));

						glTexCoord2f(right, bottom);
						glVertex2d((mapOffsetX + x * 32) + 32, (mapOffsetY + y * 32) + 32);

						glTexCoord2f(left, bottom);
						glVertex2d((mapOffsetX + x * 32),  (mapOffsetY + y * 32) + 32);
					glEnd();
				}
		
			}
		}
	}

	/* Sort all actors according to their Y position. */
	std::vector<ACTORINFO *> vecActors;
	for (CActorMap::iterator it = m_mapActors.begin(); it != m_mapActors.end(); it++)
	{
		int nActorX = 0, nActorY = 0;
		CActor *pAct = it->second;
		if (!pAct)
			continue;

		CAnimation *pAni = pAct->GetAnimation();
		if (!pAni)
			continue;

		// TODO: Do some checks to see if the actor is in viewing range.

		ACTORINFO *pActorInfo = new ACTORINFO;
		pActorInfo->x    = mapOffsetX + pAct->GetX() + pAct->GetOffsetX();
		pActorInfo->y    = mapOffsetY + pAct->GetY() + pAct->GetOffsetY() - 16;
		pActorInfo->ani  = pAni;
		pActorInfo->name = pAct->GetName();
		vecActors.push_back(pActorInfo);

		//pAni->Render(nActorX, nActorY);
	}

	CFont *pFont = CEngine::GetSingleton()->GetFont();
	int nNameWidth, nNameOffsetX, nNameOffsetY;

	std::sort(vecActors.begin(), vecActors.end(), ACTORINFO_Sort);
	for (std::vector<ACTORINFO *>::iterator it = vecActors.begin(); it != vecActors.end(); it++)
	{
		(*it)->ani->Render((*it)->x, (*it)->y);
	}

	

	/* Draw the layer about the actors. */
	if (m_pTileset &&  m_pMap->GetLayerCount() > 1)
	{
		m_pTileset->Bind();
		glColor3f(1.0f, 1.0f, 1.0f);

		int z = m_pMap->GetLayerCount() - 1;
		for (int x = startX; x < endX + 1; x++)
		{
			if (x >= m_pMap->GetWidth())
				continue;

			for (int y = startY; y < endY + 1; y++) 
			{
				TILE *t = m_pMap->At(x, y, z);
				if (!t)
					continue;

				float left   = m_flTileWidth * t->x;
				float top    = m_flTileHeight * t->y;
				float right  = left + m_flTileWidth;
				float bottom = top + m_flTileHeight;

				glBegin(GL_QUADS);
					glTexCoord2f(left, top);
					glVertex2d((mapOffsetX + x * 32),  (mapOffsetY + y * 32));

					glTexCoord2f(right, top);
					glVertex2d((mapOffsetX + x * 32) + 32, (mapOffsetY + y * 32));

					glTexCoord2f(right, bottom);
					glVertex2d((mapOffsetX + x * 32) + 32, (mapOffsetY + y * 32) + 32);

					glTexCoord2f(left, bottom);
					glVertex2d((mapOffsetX + x * 32),  (mapOffsetY + y * 32) + 32);
				glEnd();
			}
		}
	}

	/* Draw the names of all actors on the map. */
	glDisable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);
	glDisable(GL_BLEND);

	for (std::vector<ACTORINFO *>::iterator it = vecActors.begin(); it != vecActors.end(); it++)
	{
		if (pFont && (*it)->name != NULL)
		{
			nNameWidth = pFont->MeasureString((*it)->name);

			nNameOffsetX = ((*it)->x + 16) - (nNameWidth / 2);
			nNameOffsetY = ((*it)->y - (pFont->GetHeight() + 2));
		
			glColor3f(0.0f, 0.0f, 0.0f);
			pFont->Print(nNameOffsetX + 1, nNameOffsetY + 1, "%s", (*it)->name);

			glColor3f(1.0f, 1.0f, 1.0f);
			pFont->Print(nNameOffsetX, nNameOffsetY, "%s", (*it)->name);
		}
	}
}

//===============================================================
// Loads the tile set that should be used for rendering the map.
//===============================================================
void CMapScene::LoadTileset(const char *szTextureFile, const char *szDefinitionFile)
{
	if (!m_pMap)
		return;

	m_pTileset = CTextureManager::Load(szTextureFile);
	if (!m_pTileset)
		return;

	m_nTilesetWidth		= m_pTileset->GetWidth() / 32;
	m_nTilesetHeight	= m_pTileset->GetHeight() / 32;
	m_flTileWidth		= 1.0f / float(m_nTilesetWidth);
	m_flTileHeight		= 1.0f / float(m_nTilesetHeight);

	m_pMap->LoadTileDefinition(szDefinitionFile);
}
